﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using zijian666.WebApi.Attributes;
using System.Runtime.Loader;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System.ComponentModel;

namespace zijian666.WebApi
{
    /// <summary>
    /// 动态 Controller 构建器
    /// </summary>
    internal static class DynamicControllerGenerator
    {

        public static Assembly GeneratController(params Type[] interfaceTypes) => GeneratController((IEnumerable<Type>)interfaceTypes);

        public static Assembly GeneratController(IEnumerable<Type> interfaceTypes)
        {
            var assemblyName = new AssemblyName("DynamicControllers");
            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicControllers");
            var types = interfaceTypes.Select(x => GeneratController(moduleBuilder, x)).ToList();
            //AssemblyLoadContext.Default.load(assemblyName);
            return assemblyBuilder;
        }

        public static Type GeneratController(ModuleBuilder builder, Type interfaceType)
        {
            var name = interfaceType.Name.StartsWith("I") ? interfaceType.Name[1..] : interfaceType.Name;
            if (interfaceType.Name.EndsWith("Service"))
            {
                name = name[0..^7];
            }
            var typeBuilder = builder.DefineType(name + "Controller", TypeAttributes.Public, typeof(ApiControllerBase));

            // 添加特性
            var attr = interfaceType.GetCustomAttribute<HttpApiAttribute>();
            if (attr is not null && !string.IsNullOrWhiteSpace(attr.Template) && !attr.Template.Equals("[action]", StringComparison.OrdinalIgnoreCase))
            {
                typeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(RouteAttribute).GetConstructor(new[] { typeof(string) })!, new object[] { attr.Template }));
            }
            //*添加所实现的接口
            typeBuilder.AddInterfaceImplementation(interfaceType);

            // 添加代理实例字段
            var target = typeBuilder.DefineField("_target", interfaceType, FieldAttributes.Private | FieldAttributes.InitOnly);



            // 添加构造函数
            GeneratConstructor(typeBuilder, target);

            //实现代理方法
            GeneratProxyMethod(typeBuilder, target);

            var type = typeBuilder.CreateType()!;


            return type;
        }

        //private static 

        private static ConstructorBuilder GeneratConstructor(TypeBuilder builder, params FieldBuilder[] fields)
        {
            var ctor = builder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, fields.Select(x => x.FieldType).ToArray());
            var il = ctor.GetILGenerator();
            var field = fields[0];
            for (short i = 0; i < fields.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Stfld, field);
                il.Emit(OpCodes.Ret);
            }
            il.Emit(OpCodes.Ret);
            return ctor;
        }

        private static IEnumerable<MethodBuilder> GeneratProxyMethod(TypeBuilder builder, FieldBuilder field)
        {
            return field.FieldType.GetMethods().Select(x => GeneratProxyMethod(builder, field, x)).Where(x => x is not null).Select(x => x!).ToList();
        }

        private static MethodBuilder? GeneratProxyMethod(TypeBuilder builder, FieldBuilder field, MethodInfo method)
        {
            if (method.GetParameters().Any(x => x.IsOut || x.IsRetval))
            {
                return null;
            }
            var attr = new CustomAttributeBuilder(typeof(HttpApiAttribute).GetConstructors()[0], new object[0]);
            var dm = builder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, method.GetParameters().Select(x => x.ParameterType).ToArray());
            builder.DefineMethodOverride(dm, method);
            dm.SetCustomAttribute(attr);
            var il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, field);

            for (int i = 1; i <= method.GetParameters().Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i);
            }

            il.Emit(OpCodes.Callvirt, method);
            il.Emit(OpCodes.Ret);
            return dm;
        }

    }
}
